package com.example.memberinnerclass;

class ClassWithStaticMember{
    public static Object someObject;

    static {
       someObject = new String("ClassWithStaticMember.someObject的值");
    }

    // 使用private修饰PrivateStaticInnerClass，那么PrivateStaticInnerClass就只能在ClassWithStaticMember类中访问，
    // 其它类无法访问它
    private static class PrivateStaticInnerClass{
        public static void whoAmI(){
            System.out.println("I am PrivateStaticInnerClass");
        }
    }

    public static void testPrivateStaticInnerClass(){
        PrivateStaticInnerClass.whoAmI();
    }
}

public class OuterClass {
    private String message = "hello, i'm outer class";

    // 内部类可以继承一个拥有静态变量的类
    // 但是自身却不能定义静态变量，这是为何？
    // 成员内部类可以像定义类中的普通成员变量一样使用【public、private、protected、留空（包可见性）】来指定类的可见性。这点和普通的类有点不同，普通的类只有public和包可见。
    /*private*/ /*public*/ /*protected*/ class InnerClass extends ClassWithStaticMember{

        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        // 注意，内部类中不能声明static成员（变量/方法/代码块），除非这个成员是编译时常量
        // 所谓的编译时常量就是编译时就能够确定值的常量，所谓常量就是使用final修饰的成员变量
        // 具体来说，编译时常量的类型可以是int、short、byte、long、double、float、char、String等类型（不包括对应的包装类型）
        // 编译期常量意味着变量必须使用static final修饰，并且它必须在定义的时候就被赋予初值，而且初值必须是字面量。
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

        // 为什么内部类中不能定义非编译时常量的静态变量呢？（事实上，在JDK 16及更高版本中，成员内部类中可以定义静态变量/方法/代码块的）
        public static String staticStr = "这是成员内部类中定义的静态变量"; // JDK版本小于16时会编译报错，Static declarations in inner classes are not supported

        // public static final Object o = new Object(); // 编译报错，因为o不是编译时常量

        public static final int integer = 1; // OK，因为integer是一个编译时常量
        public static final short short_int = 1; // OK，因为short_int是一个编译时常量
        public static final byte byte_int = 1; // OK，因为byte_int是一个编译时常量
        public static final long long_int = 1L; // OK，是编译时常量
        public static final  double d_num = 1.2; // OK，是编译时常量
        public static final float f_num = 1.2f; // OK
        public static final char c = 'c'; // OK
        public static final String string = "hello,world"; // OK，因为string是编译器常量


        //public static final Integer i = Integer.valueOf(100); // 编译报错，因为i不是编译时常量，而是运行时常量
        //public static final Integer it =1; // 编译报错，因为这句展开来就是：“public static final Integer it = Integer.valueOf(1);”，它定义的是运行时常量。
        //public static final String string2 = new String("hhhhhh"); // 编译报错，因为string2不是编译时常量，而是运行时常量
        //public static final String xstring; // 编译报错


        // 内部类中不能定义静态代码块（事实上，在JDK 16及更高版本中，成员内部类中可以定义静态变量/方法/代码块）
        static {
            System.out.println("这是在成员内部类的静态代码块中输出的内容");
        } // Static declarations in inner classes are not supported

        // 内部类中不能定义静态方法（事实上，在JDK 16及更高版本中，成员内部类中可以定义静态变量/方法/代码块）
        //public static void say(){} // 编译报错

        // -----------------------------------------------------

        // 内部类中可以定义构造器
        public InnerClass(){}

        // 内部类中可以定义实例变量
        private Object someObj = new Object();

        // 内部类中可以定义实例方法
        private void inner_test(){
            // 实例方法中可以使用“外部类名称.this”来引用外部类的实例。
            // 这是因为内部类在编译之后会被编译器自动添加一个名为“this$0”的实例变量，
            // “this$0”变量的类型是外部类的类型，而它的值会在内部类被实例化的时候隐式地被设置为当前上下文中外部类的实例。
            // 详情可以通过反编译字节码查看。
            System.out.println(OuterClass.this.message);

            // 为什么内部类的父类中却可以包含静态变量？
            System.out.println(someObject);

            // System.out.println(staticStr);
        }
    }

    public void outer_test(){
        // 外部类的实例方法中可以直接创建内部类的实例
        InnerClass innerClass = new InnerClass();
        // 无视访问修饰符，直接访问内部类中的私有实例方法
        innerClass.inner_test();
        // 无视访问修饰符，直接访问内部类中的私有实例变量
        System.out.println(innerClass.someObj);
    }

    public static void static_test(){
        // 另外一种创建内部类实例的方法
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();
        innerClass.inner_test();
    }

    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        // outerClass.outer_test();
        // 只有在实例化内部类对象或者访问它的静态成员的时候才会加载并初始化内部类
        System.out.println(InnerClass.staticStr);

        System.out.println("------------");
        // static_test();

        // 结论：使用private修饰的成员内部类/静态内部类只能被定义它的外部类访问，其它类无法访问。（其实就和使用private修饰一个成员变量/静态变量的效果是一样的）.
        // 对于内部类，其它的protected、包可见的访问权限控制也是同样的道理。
        //ClassWithStaticMember.PrivateStaticInnerClass.whoAmI();

        // ClassWithStaticMember.testPrivateStaticInnerClass();
    }
}
